package com.google.code.jmarias.common;

import com.google.code.jmarias.common.action.AbstractAction;
import com.google.code.jmarias.common.action.AbstractSignedMessage;
import com.google.code.jmarias.common.action.AskForChoiceAction;
import com.google.code.jmarias.common.action.ChooseFromPeopleAction;
import com.google.code.jmarias.common.action.ChooseSuitAction;
import com.google.code.jmarias.common.action.CreateRoomAction;
import com.google.code.jmarias.common.action.DealFiveCardsAction;
import com.google.code.jmarias.common.action.DealFiveHiddenCardsAction;
import com.google.code.jmarias.common.action.DealSevenCardsAction;
import com.google.code.jmarias.common.action.DropTalonAction;
import com.google.code.jmarias.common.action.LoginAction;
import com.google.code.jmarias.common.action.PickCardFromPeopleAction;
import com.google.code.jmarias.common.action.PickedTrumpAction;
import com.google.code.jmarias.common.action.RoomListEcho;
import com.google.code.jmarias.common.action.SignedAction;
import com.google.code.jmarias.common.action.SimpleSystemAction;
import com.google.code.jmarias.common.action.UnhideFiveCardsAction;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;

/**
 *
 * @author Ondrej Michal
 */
public class ActionSerializer {

    private static ActionSerializer serializer = null;
    private JAXBContext context;

    public static ActionSerializer newInstance() {
        if (serializer == null) {
            serializer = new ActionSerializer();
            try {
                serializer.context = JAXBContext.newInstance(scanForJaxbClasses("com.google.code.jmarias.common"));
            } catch (JAXBException ex) {
                // @TODO tahle vyjimka by nemela byt ignorovana
                ex.printStackTrace();
            }
        }
        return serializer;
    }

    /** private */
    private ActionSerializer() {
    }

    public void serialize(AbstractSignedMessage action, PrintWriter writer) {
        serializeObject(action, writer);
    }

    public void serialize(AbstractAction action, PrintWriter writer) {
        serializeObject(action, writer);
    }

    private void serializeObject(Object o, PrintWriter writer) {
        try {
            Marshaller m = context.createMarshaller();
            m.marshal(o, writer);
            writer.println();
            writer.flush();
        } catch (JAXBException ex) {
            // @TODO tahle vyjimka by nemela byt ignorovana
            ex.printStackTrace();
        }
    }

    public AbstractSignedMessage deserializeSignedMessage(String xml) {
        return (AbstractSignedMessage) deserializeObject(xml);
    }

    public AbstractAction deserializeAbstractAction(String xml) {
        return (AbstractAction) deserializeObject(xml);
    }

    public Object deserializeObject(String xml) {
        try {
            Unmarshaller u = context.createUnmarshaller();
            return u.unmarshal(new StreamSource(new StringReader(xml)));
        } catch (JAXBException ex) {
            // @TODO tahle vyjimka by nemela byt ignorovana
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * Jaxb zrejme neumi tridy skenovat v zadanem baliku. Proto jsem chtel neco
     * takoveho udelat rucne, ale nepodarilo se. Pres reflexi to nejde, protoze
     * ta neumoznuje ziskat tridy baliku. Jiny zpusob s classloaderem zase neumi
     * tridy extrahovat z externiho jaru. */
    private static Class[] scanForJaxbClasses(String packageName) {
        List<Class> result = new ArrayList<Class>();
        result.add(Token.class);
        result.add(AbstractUser.class);
        result.add(SignedAction.class);
        result.add(RegisteredUser.class);
        result.add(ChooseFromPeopleAction.class);
        result.add(CreateRoomAction.class);
        result.add(DealFiveCardsAction.class);
        result.add(DealFiveHiddenCardsAction.class);
        result.add(UnhideFiveCardsAction.class);
        result.add(DealSevenCardsAction.class);
        result.add(LoginAction.class);
        result.add(PickedTrumpAction.class);
        result.add(RoomListEcho.class);
        result.add(SimpleSystemAction.class);
        result.add(PickCardFromPeopleAction.class);
        result.add(AskForChoiceAction.class);
        result.add(DropTalonAction.class);
        result.add(ChooseSuitAction.class);
//        result.add(SendTalonAction.class);

        return result.toArray(new Class[0]);
    }

}
